package com.yf.controller;

import com.yf.bean.*;
import com.yf.entity.BreakpointEntity;
import com.yf.entity.RestPasswdEntity;
import com.yf.service.CommonService;
import com.yf.service.RedisService;
import com.yf.service.UserService;
import com.yf.utils.UserContext;
import com.yf.utils.Utils;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.codec.digest.MurmurHash3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

@Log4j2
@RestController
@RequestMapping(value = "${custom.api-version}")
public class UserController {
    UserService userService;
    CommonService commonService;
    RedisService redisService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Autowired
    public void setCommonService(CommonService commonService) {
        this.commonService = commonService;
    }

    @Autowired
    public void setRedisService(RedisService redisService) {
        this.redisService = redisService;
    }

    @PostMapping("/register")
    public BaseResponse<Boolean> register(
//            @RequestParam(defaultValue = "#") String token,
            @RequestParam(defaultValue = "#") String regCode,
            @RequestBody UserBean user) {
//        if (Utils.isNullOrEmpty(token) || token.equals("#")) {
//            return BaseResponse.of(StatusCode.USER_RECAPTCHA_MISS);
//        }

        if (Utils.isNullOrEmpty(regCode) || regCode.equals("#")) {
            return BaseResponse.of(StatusCode.USER_REG_CODE);
        }

        if (!user.valid()) {
            return BaseResponse.of(StatusCode.USER_INVALID_PARAMS);
        }

//        log.info("token:" + token);
//        log.info("regCode:" + regCode);
//        if (!commonService.validateRecaptcha(token)) {
//            return BaseResponse.of(StatusCode.USER_RECAPTCHA_FAIL);
//        }


//        final String email = user.getEmail();
//        if (email.lastIndexOf("qq.com") != -1 && email.length() < 16) {
//            return BaseResponse.of(StatusCode.USER_REG_FAKE_EMAIL);
//        }

        if (!redisService.containsRegCode(regCode)) {
            return BaseResponse.of(StatusCode.USER_INVALID_REG_CODE);
        }
        // transform info
        user.transform();
        user.setCreated(Utils.currentTime());
        user.setValid(Boolean.TRUE);
        int id = userService.register(user);

        if (id < 0) {
            return BaseResponse.of(StatusCode.USER_EXISTED);
        }

        if (id > 0) {
            redisService.validateRegCode(regCode);
//            final String activateCode = Utils.encodeInt(id);
//            boolean ans = commonService.sendActivateEmail(user.getEmail(), activateCode);
            return BaseResponse.success(Boolean.TRUE);
        }

        return BaseResponse.of(StatusCode.USER_ADD_FAIL);
    }

    @GetMapping("/register/activate")
    public BaseResponse<String> userActivate(@RequestParam(name = "token", defaultValue = "#") String token) {
        if (Utils.isNullOrEmpty(token) || token.length() != 25) {
            return BaseResponse.fail("激活码有问题");
        }
        final int uid = Utils.decodeUserId(token);
//        log.error("User id:" +uid);
        final int ret = userService.activateUser(uid);
        String msg;
        if (ret > 0) {
            msg = "激活成功 请返回主页登录.";
        } else {
            msg = "激活失败 稍后再试";
        }
        return BaseResponse.success(msg);
    }

    @GetMapping("/register/check")
    public BaseResponse<Boolean> checkUserInfo(
            @RequestParam(name = "f", defaultValue = "#") String field,
            @RequestParam(name = "v", defaultValue = "#") String value) {
        if (field.equals("#") ||value.equals("#")) {
            return BaseResponse.fail();
        }
        boolean ret = false;
        if (field.equals("name")) {
            ret = userService.existUser(MurmurHash3.hash32x86(value.getBytes()));
        } else if (field.equals("email")) {
            ret = userService.existEmail(MurmurHash3.hash32x86(value.getBytes()));
        }
        return BaseResponse.success(ret);
    }

    @PostMapping("/login")
    public BaseResponse<Map<String, String>> login(@RequestBody UserBean user, HttpServletRequest request) {
        if (user == null || Utils.isNullOrEmpty(user.getEmail()) || Utils.isNullOrEmpty(user.getPasswd())) {
            log.error("User failed");
            return BaseResponse.fail();
        }
        if (user.getEmail().length() > 30 || user.getPasswd().length() > 30) {
            log.error("Passwd failed");
            return BaseResponse.fail();
        }
        user.transform();
        final UserBean u = userService.login(user);
        if (u == null) {
            return BaseResponse.of(StatusCode.USER_LOG_NOT_EXIST);
        }

        int uid = u.getId();
        String agent = request.getHeader("User-Agent");
        String value;
        if (Utils.isNullOrEmpty(agent)) {
            value = Utils.defaultAgent;
        } else {
            value = Utils.digestPasswd(agent);
        }

        final boolean r = redisService.addLoginSession(String.valueOf(uid), value);
        if (!r) {
            log.error("Redis failed");
            return BaseResponse.fail();
        }
        Map<String, String> token = Utils.encodeUserId(uid);
        token.put("er", Utils.encodeInt(u.getRole()));
        token.put("uname", String.valueOf(u.getName()));
        return BaseResponse.success(token);
    }

    /**
     * 生成注册码
     * @return
     */
//    @PostMapping("/register/code")
//    public BaseResponse<Integer> generateRegCode() {
//        int codeNum = redisService.genRegCode();
//        return BaseResponse.success(codeNum);
//    }

    /**
     * 获取注册码
     * @return
     */
    @GetMapping("/register/code")
    public BaseResponse<String> getRegCode(@RequestParam(value = "auth", defaultValue = "#") String auth) {
        if (!auth.equals("regCode")) {
            return BaseResponse.success();
        }
        String ret = redisService.getRegCode();
        return BaseResponse.success(ret);
    }

    /**
     * 验证注册码
     * @return
     */
//    @PutMapping("/register/code")
//    public BaseResponse<Boolean> validateRegCode(@RequestParam(defaultValue = "#") String token) {
//        if (token == null || token.equals("#") || token.length() != Utils.regCodeLen) {
//            return BaseResponse.fail(false);
//        }
//        return BaseResponse.success(redisService.validateRegCode(token));
//    }

    @PostMapping("/user/statistic")
    public BaseResponse<Map<String, Object>> getUserInfo(
            @RequestParam(name = "op", defaultValue = "0") String id) {
        if (Utils.isNullOrEmpty(id) || id.equals("0")) {
            return BaseResponse.fail();
        }
        final int uid = Utils.decodeUserId(id);
        if (uid <= 0) {
            return BaseResponse.fail();
        }
        Map<String, Object> ret = new HashMap<>();
        final UserKickLog userKickStat = userService.getUserKickStat(uid);
        if (userKickStat != null) {
            Integer right = userKickStat.getRight_num();
            Integer total = userKickStat.getKick_num();
            Integer netKickNum = userKickStat.getNet_kick_num();
            ret.put("total", total);
            ret.put("correct", right);
            ret.put("pure", netKickNum);
        } else {
            ret.put("total", 0);
            ret.put("correct", 0);
            ret.put("pure", 0);
        }

        String rank = redisService.getUserInfoLong(uid, "rank");
        String rankClass = redisService.getUserInfoLong(uid, "rankClass");
        Integer qusTotalNum = redisService.getQusTotalNum();
        rank = rank == null ? "1" : rank;
        rankClass = rankClass == null ? "塑料" : rankClass;
        qusTotalNum = qusTotalNum == null ? 167 : qusTotalNum;

        ret.put("rank", rank);
        ret.put("rankClass", rankClass);
        ret.put("qusTotal", qusTotalNum);

        Map<String, Object> subjectQusInfo = redisService.getSubjectQusInfo();
        for (Map.Entry<String, Object> e :subjectQusInfo.entrySet()) {
            ret.put(e.getKey(), e.getValue());
        }
        return BaseResponse.success(ret);
    }

    @PostMapping("/solve/log")
    public BaseResponse<Boolean> addSolveLog(@RequestBody List<SolveLogBean> logs) {
        final Logger logger = LogManager.getLogger("user.kick.recorder");
        for (SolveLogBean bean: logs) {
            logger.info(bean.toString());
        }
        final int i = userService.addLogStatistic(logs);
        if (i > 0) {
            return BaseResponse.success();
        }
        return BaseResponse.fail();
    }

    @GetMapping("/user/ranks")
    public BaseResponse<List<String>> getUserRanking() {
        final List<String> ranking = redisService.getRanking();
        return BaseResponse.success(ranking);
    }


    @PostMapping("/user/collect")
    public BaseResponse<List<CollectionBean>> addUserCollect(@RequestBody List<CollectionBean> collect) {
        if (collect == null || collect.size() == 0) {
            return BaseResponse.success();
        }
        final Integer uid = UserContext.getUserId();
        if (uid <= 0) {
            return BaseResponse.fail();
        }
        for (CollectionBean bean: collect) {
            bean.setUid(uid);
        }
        final int i = userService.addCollect(collect);
        if (i > 0) {
            return BaseResponse.success(collect);
        }
        return BaseResponse.fail();
    }

    @GetMapping("/user/collect")
    public BaseResponse<List<CollectionBean>> selectUserCollect() {
        final Integer uid = UserContext.getUserId();
        if (uid <= 0) {
            return BaseResponse.success();
        }
        final List<CollectionBean> collectionBeans = userService.selectCollect(uid);
        return BaseResponse.success(collectionBeans);
    }

    @GetMapping("/user/collect/sub")
    public BaseResponse<List<CollectionBean>> getCollectBySub(@RequestParam(name = "cate") Integer cate) {
        final Integer uid = UserContext.getUserId();
        if (uid <= 0) {
            return BaseResponse.success();
        }
        final List<CollectionBean> collectBySub = userService.getCollectBySub(uid, cate);
        return BaseResponse.success(collectBySub);
    }

    @GetMapping("/user/collect/delete")
    public BaseResponse<Boolean> deleteCollect(@RequestParam(name = "id", defaultValue = "0") Integer id) {
        if (id.equals(0)) {
            return BaseResponse.fail();
        }
        boolean ret = userService.deleteCollect(id) > 0;
        return BaseResponse.success(ret);
    }

//    用于检查用户异常行为
//    @PostMapping("/user/login")
//    public BaseResponse<Object> blackUser() {
//        System.out.println("OK");
//        return BaseResponse.success();
//    }

    @PostMapping("/user/breakpoint")
    public BaseResponse<Boolean> addBreakpoint(@RequestBody BreakpointBean breakpoint) {
        return BaseResponse.success(userService.addBreakpoint(breakpoint));
    }

    @GetMapping("/user/breakpoint")
    public BaseResponse<List<BreakpointEntity>> getBreakpoint(){
        return BaseResponse.success(userService.getUserBreakpoint());
    }

    @GetMapping("/user/reset/pass")
    public BaseResponse<String> resetUserPass(
            @RequestParam(name = "user", defaultValue = "#") String user,
            @RequestParam(name = "pass", defaultValue = "#") String pass,
            @RequestParam(name = "auth", defaultValue = "#") String auth){
        if (user.equals("#") || auth.equals("#")) {
            return BaseResponse.fail();
        }
        if (!auth.equals("dypi_go")) {
            return BaseResponse.fail();
        }
        UserBean userBean = new UserBean();
        String originPass;
        if (pass.equals("#")) {
            originPass = "dypi.xyz";
            userBean.setPasswd(originPass);
        } else {
            originPass = pass;
            userBean.setPasswd(pass);
        }
        userBean.setEmail(user);
        userBean.transform();
        if (!userService.resetUserPass(userBean)) {
            return BaseResponse.success("fail");
        }
        return BaseResponse.success(originPass);
    }


    @GetMapping("/user/review")
    public BaseResponse<String> getUserReview() {
        final String userReviewList = userService.getUserReviewList();
        if (userReviewList == null) {
            return BaseResponse.of(StatusCode.USER_REVIEW_NULL);
        }
        return BaseResponse.success(userReviewList);
    }

    @GetMapping("/user/history/answer")
    public BaseResponse<Map<String, String>> getUserHistoryAnswer(@RequestParam(name = "subject", defaultValue = "0") String prefix) {
        final Integer uid = UserContext.getUserId();
        if (uid <= 0) {
            return BaseResponse.fail();
        }
        final Map<String, String> userHistoryAnswer = redisService.getUserHistoryAnswer(uid, prefix);
        if (userHistoryAnswer.size() > 0) {
            return BaseResponse.success(userHistoryAnswer);
        }
        final List<HistoryAnswer> historyAns = userService.getUserHistoryAnswer(uid, Integer.parseInt(prefix));
        if (historyAns.size() > 0) {
            redisService.setUserHistoryAnswer(uid, historyAns, prefix, true);
        }
        final Map<String, String> answer = redisService.getUserHistoryAnswer(uid, prefix);
        return BaseResponse.success(answer);
    }

    @PostMapping("/user/history/answer")
    public BaseResponse<Boolean> setUserHistoryAnswer(@RequestParam(name = "subject", defaultValue = "#") String prefix, @RequestBody List<HistoryAnswer> bean) {
        if (prefix.equals("#")) {
            return BaseResponse.fail();
        }
        final Integer uid = UserContext.getUserId();
        if (uid <= 0) {
            return BaseResponse.fail();
        }
        for (HistoryAnswer ans: bean) {
            ans.setUid(uid);
        }
        final Boolean ret = redisService.setUserHistoryAnswer(uid, bean, prefix, false);
        return BaseResponse.success(ret);
    }

    @PostMapping("/user/reset/passwd")
    public BaseResponse<Object> resetUserPass(@RequestBody RestPasswdEntity bean) {
        if (bean == null) {
            return BaseResponse.fail();
        }

        if (Utils.isNullOrEmpty(bean.getEmail())) {
            return BaseResponse.fail();
        }

        if (Utils.isNullOrEmpty(bean.getPasswd())) {
            return BaseResponse.fail();
        }

        if (Utils.isNullOrEmpty(bean.getValidCode())) {
            return BaseResponse.fail();
        }

        if (!redisService.existKey(bean.getEmail())) {
            return BaseResponse.of(StatusCode.USER_REST_VALID_CODE_NULL);
        }

        final String key = redisService.getKey(bean.getEmail());
        if (key == null) {
            return BaseResponse.of(StatusCode.USER_REST_VALID_CODE_EXPIRE);
        }

        if (!key.equals(bean.getValidCode())) {
            return BaseResponse.of(StatusCode.USER_REST_VALID_CODE_INVALID);
        }

        UserBean userBean = new UserBean();
        userBean.setPasswd(bean.getPasswd());
        userBean.setEmail(bean.getEmail());
        userBean.transform();
        boolean ret  = userService.resetUserPass(userBean);
        if (!ret) {
            return BaseResponse.fail();
        }
        return BaseResponse.success();
    }

    @GetMapping("/user/reset/code")
    public BaseResponse<Integer> getValidCode(@RequestParam(name = "email", defaultValue = "") String email) {
        if (Utils.isNullOrEmpty(email)) {
            return BaseResponse.fail();
        }

        final int i = commonService.sendEmailValidCode(email);
        if (i == 1) {
            return BaseResponse.of(StatusCode.USER_REST_VALID_CODE_ALREADY);
        }

        if (i == 2) {
            return BaseResponse.of(StatusCode.USER_REST_VALID_CODE_ERROR);
        }

        return BaseResponse.success();
    }

    @GetMapping("/user/reset/name")
    public BaseResponse<Boolean> resetUserName(
            @RequestParam(name = "name", defaultValue = "") String name,
            @RequestParam(name = "token", defaultValue = "") String token) {
        if (Utils.isNullOrEmpty(name)) {
            return BaseResponse.fail();
        }
        if (name.length() > 20) {
            return BaseResponse.fail();
        }
        final int uid = Utils.decodeUserId(token);
        if (uid <= 0) {
            return BaseResponse.fail();
        }

        UserBean bean = new UserBean();
        bean.setId(uid);
        bean.setName(name);
        bean.transform();
        return BaseResponse.success(userService.resetUserName(bean));
    }
}
